/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) GenKeyPair.java 2018-08-06 17:26
 */

package cn.jh.common.core.crypto;

import org.inferred.freebuilder.FreeBuilder;
import org.jetbrains.annotations.Nullable;


/**
 * {@code keytool -genkeypair} 命令参数构建模型。
 *
 * @author Fuchun
 * @since 1.0
 */
@FreeBuilder
public abstract class GenKeyPair implements GenKey {

    protected static final String SHA1withDSA = "SHA1withDSA";
    protected static final String SHA1withRSA = "SHA1withRSA";
    protected static final String SHA256withRSA = "SHA256withRSA";
    protected static final String SHA256withECDSA = "SHA256withECDSA";

    public static Builder rsa() {
        return new Builder()
                .setKeyAlg("RSA")
                .setSigAlg(SHA256withRSA)
                .setKeySize(2048);
    }

    public static Builder dsa() {
        return new Builder()
                .setKeyAlg("DSA")
                .setSigAlg(SHA1withDSA)
                .setKeySize(1024);
    }

    public static Builder ec() {
        return new Builder()
                .setKeyAlg("EC")
                .setSigAlg(SHA256withECDSA)
                .setKeySize(256);
    }

    private transient String cmdString = null;

    /**
     * 签名算法名称。
     * <pre>{@code
     * Default value:
     * keyAlg=DSA => SHA1withDSA
     * keyAlg=RSA => SHA256withRSA
     * keyAlg=EC  => SHA256withECDSA
     * }</pre>
     */
    public abstract String getSigAlg();

    /** 目标别名。*/
    @Nullable
    public abstract String getDestAlias();

    /**
     * 唯一判别名。
     * cn=test,ou=test,o=test,l=Shanghai,st=Shanghai,c=CN
     */
    public abstract DName getDName();

    /**
     * 证书有效期开始日期
     */
    @Nullable
    public abstract String getStartDate();

    /** X.509 扩展 */
    @Nullable
    public abstract String getExt();

    /** 有效天数。*/
    public abstract int getValidity();

    public static class Builder extends GenKeyPair_Builder {

        public Builder() {
            initDefaults();
        }

        private void initDefaults() {
            setKeyAlg("DSA");
            setAlias("mykey");
            setValidity(90);
            setKeySize(-1);
            setView(false);
            setSigAlg("");
        }

        @Override
        public Builder setStorePass(String storePass) {
            Builder b = super.setStorePass(storePass);
            if (getKeyPass() == null) {
                setKeyPass(storePass);
            }
            return b;
        }

        public Builder sha1WithRSA() {
            setSigAlg(SHA1withRSA);
            return this;
        }

        @Override
        public GenKeyPair build() {
            String keyAlg = getKeyAlg();
            int defaultKeySize;
            String defaultSigAlg;
            switch (keyAlg) {
                case "RSA":
                    defaultKeySize = 2048;
                    defaultSigAlg = SHA256withRSA;
                    break;
                case "DSA":
                    defaultKeySize = 1024;
                    defaultSigAlg = SHA1withDSA;
                    break;
                case "EC":
                    defaultKeySize = 256;
                    defaultSigAlg = SHA256withECDSA;
                    break;
                default:
                    throw new IllegalArgumentException("Not support `" + keyAlg + "` algorithm.");
            }
            if (getKeySize() == -1) setKeySize(defaultKeySize);
            if (getSigAlg().isEmpty()) setSigAlg(defaultSigAlg);
            return super.build();
        }
    }

    @Override
    public String toCmdString() {
        if (cmdString != null) return cmdString;
        StringBuilder sb = new StringBuilder("-genkeypair");
        sb.append(" -alias ").append(getAlias());
        sb.append(" -keyalg ").append(getKeyAlg());
        sb.append(" -keysize ").append(getKeySize());
        sb.append(" -sigalg ").append(getSigAlg());
        if (getDestAlias() != null) {
            sb.append(" -destalias ").append(getDestAlias());
        }
        sb.append(" -dname \"").append(DName.toString(getDName())).append("\"");
        if (getStartDate() != null) {
            sb.append(" -startdate ").append(getStartDate());
        }
        if (getExt() != null) {
            sb.append(" -ext ").append(getExt());
        }
        sb.append(" -validity ").append(getValidity());
        sb.append(" -keypass ").append(getKeyPass());
        sb.append(" -keystore ").append(getKeyStore());
        sb.append(" -storepass ").append(getStorePass());
        if (getStoreType() != null) {
            sb.append(" -storetype ").append(getStoreType());
        }
        appendProvider(sb);
        if (isView()) {
            sb.append(" -v");
        }
        cmdString = sb.toString();
        return cmdString;
    }
}
